﻿using System;
using System.Collections.Generic;
using System.Text;
using OpenGLES;

namespace PFD
{
    public class VertexBufferOGL : VertexBufferBase
    {
        /// <summary>
        /// Provide a flag to indicate whether or not this object has already been disposed.
        /// </summary>
        bool Disposed = false;

        /// <summary>
        /// Store the OpenGL Vertex Buffer Name
        /// </summary>
        uint nOGLBufferName;

        /// <summary>
        /// Public constructor. Create a vertex buffer object from OpenGL for the given
        /// number of vertices of the given type
        /// </summary>
        /// <param name="numVerts"></param>
        /// <param name="vet"></param>
        unsafe public VertexBufferOGL(uint numVerts, VertexElementType vet)
            : base(numVerts, vet)
        {
            //  Create the actual array
            array = new float [numVerts * vertex_stride];
            //  create the opengl buffer
            fixed (uint* pName = &nOGLBufferName)
            {
                gl.GenBuffers(1, pName);
            }
        }

        /// <summary>
        /// Lock function. For this implementation version, the OpenGL vertex buffer
        /// is not really used. So locking does not do anything.
        /// </summary>
        protected override void Lock()
        {
            // do nothing for OGL as the array is always
            // in memory
        }

        /// <summary>
        /// Unlock function. In this implementation version OpenGL vertex buffers are not yet 
        /// used so the use of unlock does not require any implementation.
        /// </summary>
        protected override void Unlock()
        {
            // do nothing for OGL as the array is always
            // in memory
        }

        /// <summary>
        /// RealizeVertexBuffers makes the vertex buffers actual for OpenGl. It automatically sets all
        /// relevant client states in the OpenGl rendering machine. 
        /// </summary>
        /// <param name="rd"></param>
        /// <param name="AllowMultitexture"></param>
        /// <param name="selectedTexture"></param>
        /// <returns></returns>
        unsafe public override bool RealizeVertexBuffer(IRenderDevice rd, bool AllowMultitexture, uint selectedTexture)
        {
            RenderDeviceOGL rdogl = (RenderDeviceOGL)rd;
            fixed (float* pNArray = &array[NormalOffset])
            fixed (float* pVArray = &array[0])
            fixed (float* pCArray = &array[ColorOffset])
            fixed (float* pT1Array = &array[TextureSet1Offset])
            fixed (float* pT2Array = &array[TextureSet2Offset])
            {
                if (AllowMultitexture || (selectedTexture == 0))
                {
                    gl.EnableClientState(gl.GL_VERTEX_ARRAY);
                    gl.VertexPointer(3, gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pVArray);
#if DEBUG
                    CheckError("Error setting VertexPointer in function RealizeVertexBuffer");
#endif
                    if (bContainsNormals)
                    {
                        gl.EnableClientState(gl.GL_NORMAL_ARRAY);
                        gl.NormalPointer(gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pNArray);
#if DEBUG
                        CheckError("Error setting NormalPointer in function RealizeVertexBuffer");
#endif
                    }
                    else
                        gl.DisableClientState(gl.GL_NORMAL_ARRAY);

                    if (bContainsColors)
                    {
                        gl.EnableClientState(gl.GL_COLOR_ARRAY);
                        gl.ColorPointer(4, gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pCArray);
#if DEBUG
                        CheckError("Error setting ColorPointer in function RealizeVertexBuffer");
#endif
                    }
                    else
                        gl.DisableClientState(gl.GL_COLOR_ARRAY);
                }
                if (AllowMultitexture)
                {
                    gl.ActiveTexture(gl.GL_TEXTURE0);
                    gl.ClientActiveTexture(gl.GL_TEXTURE0);
                    if (bContainsTextureSet1)
                    {
                        gl.Enable(gl.GL_TEXTURE_2D);
                        gl.EnableClientState(gl.GL_TEXTURE_COORD_ARRAY);
                        gl.TexCoordPointer(2, gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pT1Array);
#if DEBUG
                        CheckError("Error setting TexCoordPointer for Texture0 in function RealizeVertexBuffer");
#endif
                    }
                    else
                    {
                        gl.Disable(gl.GL_TEXTURE_2D);
                        gl.DisableClientState(gl.GL_TEXTURE_COORD_ARRAY);
                    }

                    gl.ActiveTexture(gl.GL_TEXTURE1);
                    gl.ClientActiveTexture(gl.GL_TEXTURE0);
                    if (bContainsTextureSet2)
                    {
                        gl.Enable(gl.GL_TEXTURE_2D);
                        gl.EnableClientState(gl.GL_TEXTURE_COORD_ARRAY);
                        gl.TexCoordPointer(2, gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pT2Array);
#if DEBUG
                        CheckError("Error setting TexCoordPointer for Texture1 in function RealizeVertexBuffer");
#endif
                    }
                    else
                    {
                        gl.Disable(gl.GL_TEXTURE_2D);
                        gl.DisableClientState(gl.GL_TEXTURE_COORD_ARRAY);
                    }
                }
                else
                {
                    if (bContainsTextureSet1 && (selectedTexture == 0))
                    {
                        gl.EnableClientState(gl.GL_TEXTURE_COORD_ARRAY);
                        gl.TexCoordPointer(2, gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pT1Array);
#if DEBUG
                        CheckError("Error setting TexCoordPointer for Single Texture in function RealizeVertexBuffer");
#endif
                    }
                    else if (bContainsTextureSet2 && (selectedTexture == 1))
                    {
                        gl.EnableClientState(gl.GL_TEXTURE_COORD_ARRAY);
                        gl.TexCoordPointer(2, gl.GL_FLOAT, (int)vertex_stride * sizeof(float), (IntPtr)pT2Array);
#if DEBUG
                        CheckError("Error setting TexCoordPointer for Single Texture in function RealizeVertexBuffer");
#endif
                    }
                }

            }
            return true;
        }

        /// <summary>
        /// Dispose function. Disposes this object and all related resources.
        /// </summary>
        unsafe public override void Dispose()
        {
            if (!Disposed)
            {
                fixed (uint* pName = &nOGLBufferName)
                    gl.DeleteBuffers(1, pName);
                Disposed = true;
            }
        }

        /// <summary>
        /// This functions check for an OpenGL error. In case an error was reported
        /// the function throws an exception using the provided msg string.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected override bool CheckError(string msg)
        {
            uint error = gl.GetError();

            if (error != 0)
            {
                throw new Exception(msg + " OpenGLES Error detected : " + error.ToString());
            }

            return false;
        }

    }
}
